﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using Db4objects.Db4o;
using DocManagerORM.ORM;
using System.Data.Objects.DataClasses;
using System.Data.Objects;
using System.Reflection;
using DS2171Utils;
using System.Collections;
using System.Data;
using System.Windows.Controls;
using System.Data.Metadata.Edm;


namespace DocManagerORM.Utils
{
    public class BindingListExt<T> : BindingList<T>, ISalva, IPaginator where T : EntityObject, new() 
    {
        
        public IList<KeyValuePair<string, string>> ListaValidazione { get; set; }
        public bool UsaPaginatore { get; set; }
        
        DocManagerDbEntities m_Entity = null;//  DocManagerDbEntities

        MethodInfo _MethodInfo = null;
        MemberInfo[] _MemberInfo = null;
        FieldInfo  _FieldInfo = null;
        PropertyInfo _PropInfo = null;
        EntityKey _EntKey;

        string m_NomeEntità = string.Empty;
        public Type Tipo { get; set; }
        public string NomeOggetto { get; set; }
        private T OggettoModificato = default(T);
                
        public BindingListExt(IList<T> PLista):base(PLista)
        {
            Inizializza();
        }

  
        public BindingListExt( IList<T> PLista, ref DocManagerDbEntities pEntityManager,
            IList<KeyValuePair<string, string>> pDatiValidazione = null,bool pUsaPaginatore= false)   
            :base(PLista)
        {            
            m_Entity = pEntityManager;            
            ListaValidazione = pDatiValidazione;
            UsaPaginatore = pUsaPaginatore;

            Inizializza();
            InitPaginator();
        }

        public BindingListExt( ref DocManagerDbEntities pEntityManager,
           IList<KeyValuePair<string, string>> pDatiValidazione = null, bool pUsaPaginatore = false)
         {
            m_Entity = pEntityManager;
            ListaValidazione = pDatiValidazione;
            UsaPaginatore = pUsaPaginatore;

            Inizializza();
            InitPaginator();
        }


        void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {            
           if (ListaValidazione == null) return;
           var datoValidazione = ListaValidazione.Where(x=> x.Key.Equals(e.PropertyName)).FirstOrDefault();
           if (datoValidazione.Value == null) return;

           _PropInfo = sender.GetType().GetProperty(e.PropertyName);

           var valo = _PropInfo.GetValue(sender, null);

           var tipoCampo = Type.GetType(datoValidazione.Value);

           _MethodInfo = tipoCampo.GetMethod("Parse",new Type[] { typeof(int)});

           try
           {
               if (_MethodInfo != null) _MethodInfo.Invoke(sender, new[] { valo });
           }
           catch (Exception ex)
           {
            throw new Exception("Errore : \r\n"+ ex.Message);
           }
            
        }//fM

        private object getValueField(string pNameField,object pOggetto)
        {
            _FieldInfo = null;
            _FieldInfo = typeof(T).GetField(pNameField, BindingFlags.Public | BindingFlags.Instance);
            var valo = _FieldInfo.GetValue(pOggetto);
            return valo;
        }//fM


        internal  EntitySetBase GetEntitySet<T>(ObjectContext context)
        {
            EntityContainer container =
                    context.MetadataWorkspace
                        .GetEntityContainer(context.DefaultContainerName, DataSpace.CSpace);
            Type baseType = GetBaseType(typeof(T));
            EntitySetBase entitySet = container.BaseEntitySets
                .Where(item => item.ElementType.Name.Equals(baseType.Name))
                .FirstOrDefault();

            return entitySet;
        }

        private  Type GetBaseType(Type type)
        {
            var baseType = type.BaseType;
            if (baseType != null && baseType != typeof(EntityObject))
            {
                return GetBaseType(type.BaseType);
            }
            return type;
        }


        public void getMetadata(ObjectContext context,string pName)
        {
            var query = from meta in context.MetadataWorkspace.GetItems(DataSpace.CSpace)
              .Where(m => m.BuiltInTypeKind == BuiltInTypeKind.EntityType)
                        let m = (meta as EntityType)
                        let properties = m.Properties
                        select new
                        {
                            EntityName = m.Name,
                            MembersCount = m.Members.Count,
                            KeyMembersCount = m.KeyMembers.Count,
                            PropertyNames = from p in properties
                                            select new
                                            {
                                                p.Name,
                                                p.Nullable,
                                                p.DefaultValue,
                                                Documentation = p.Documentation != null ? p.Documentation.LongDescription : null,
                                                Type = p.TypeUsage.EdmType.Name
                                            }
                        };

            var obj = query.Where(x => x.EntityName == pName).FirstOrDefault().PropertyNames.ToList(); 
            
            
        
        }//fM

        private void Inizializza()
        {
            Tipo = new T().GetType();
            m_NomeEntità = new T().GetType().Name;
            NomeOggetto = m_NomeEntità;

            getMetadata(m_Entity, NomeOggetto);   
            //bool f = PLista.IsReadOnly;
            AllowEdit = true; AllowNew = true;
            AllowRemove = true; RaiseListChangedEvents = true;
             
            if (this.Items.Count > 0)
            {
                foreach (var item in this.Items)
                    item.PropertyChanged += new PropertyChangedEventHandler(item_PropertyChanged);

                _EntKey = ((IEntityWithKey)this.Items[0]).EntityKey; // 
            }
             
            var es = GetEntitySet<T>(m_Entity).Documentation      ;
            
            //_EntKey = ((IEntityWithKey)es).EntityKey; // 
            
            //
            AddingNew += (ob, ev) =>
            {
                if (ev.NewObject == null)
                    ev.NewObject = new T();                           
                
                aggiungiNuovoInEntity(ev.NewObject);
            };
            
            ListChanged += (ob, ev) =>
            {
                var lista = ob as IBindingList;
                if (lista == null || lista.Count == 0) return;
                OggettoModificato = lista[ev.NewIndex] as T;

                var key = ((IEntityWithKey)OggettoModificato).EntityKey;

                object oggetto = null;
                m_Entity.TryGetObjectByKey(key,out oggetto );

                var statoObj = OggettoModificato.EntityState;

                //var rel = m_Entity.ObjectStateManager.GetRelationshipManager(obj).GetAllRelatedEnds().ToList();
                System.Data.Objects.ObjectStateEntry stato = null;
                var ok = m_Entity.ObjectStateManager.TryGetObjectStateEntry(OggettoModificato, out   stato);

                if (oggetto == null)
                {
                    aggiungiNuovoInEntity(OggettoModificato);
                    //m_Entity.ObjectStateManager.ChangeObjectState(obj, System.Data.EntityState.Modified);
                }                         
               
            };
        }//fM
        


        private void aggiungiNuovoInEntity(object  pEntità)
        {
            if (pEntità == null) return ;
            m_Entity.AddObject(m_NomeEntità,pEntità) ;
        }

        public int salvaDati()
        {
            try
            {
                var salvati = m_Entity.SaveChanges(SaveOptions.AcceptAllChangesAfterSave);
                return salvati;
            }
            catch (Exception ex)
            {
                throw;
            }        
        
        }//fM


        public void addNewOggetto()
        {
            this.Add(new T()); 
            
        }

        #region PAGINAZIONE

        public ItemsControl DysplayerControl { get; set; }

        public void InitPaginator()
        {
            if (_EntKey == null)
            {
                QueryDati = m_Entity.CreateQuery<T>("select value o from " + m_NomeEntità + " as o").Top("1") ;
                var en = QueryDati.FirstOrDefault();
                if(en != null) _EntKey = en.EntityKey;
            }

            if (_EntKey != null)
                QueryDati = m_Entity.CreateQuery<T>("select value o from " + m_NomeEntità + " as o")
                    .OrderBy(" it." + _EntKey.EntityKeyValues[0].Key.ToString()); ;


            NumeroPagina = 1;
            QuantielementiPerpagina = 20;
            QuantePagine = GetNumPagineTotali();

            if (UsaPaginatore) GetPaginaDati(1);
            
        }

        private void CaricaLista(IEnumerable<T> pDati)
        {
           
            this.ClearItems();
            
             foreach (var item in pDati)
	            this.Items.Add(item) ;

             foreach (var item in this.Items)
                 item.PropertyChanged += new PropertyChangedEventHandler(item_PropertyChanged);

             if (DysplayerControl != null) DysplayerControl.Items.Refresh(); 
            
        }
            

        public int QuantePagine
        {
           get;set;
        }

        [System.ComponentModel.DefaultValue(23)] 
        public int QuantielementiPerpagina
        {
            get; set;
        }

        public int NumeroPagina 
        {
           get;set;
        }

        /// <summary>
        /// Parte dalla pagina n# 1
        /// </summary>
        public void GetPaginaDati(int pNumPagina)
        {
            if (RicalcolaPagine)
            {
                GetNumPagineTotali();
                RicalcolaPagine = false;
            }
            IList<T> dati;

            if (QuantielementiPerpagina < 1) QuantielementiPerpagina = 20;
            NumeroPagina = pNumPagina;
            if (NumeroPagina < 1) NumeroPagina = 1;
            if (NumeroPagina > QuantePagine) NumeroPagina = QuantePagine;

            if (NumeroPagina == 1)
                dati = QueryDati.Take(QuantielementiPerpagina).ToList();
            else
                dati = QueryDati.Skip((pNumPagina-1) * QuantielementiPerpagina).Take(QuantielementiPerpagina).ToList();

            CaricaLista(dati);

            //return dati as IList;

        }//fM

        public int GetNumPagineTotali()
        {
             if (QuantielementiPerpagina < 1) QuantielementiPerpagina = 20;
            var tot = QueryDati.Count();
            if (QuantielementiPerpagina > tot) return 1;
            
            var add = (tot % QuantielementiPerpagina) > 0 ? true:false ;
            var totPagine = tot / QuantielementiPerpagina;
            if (add) ++totPagine;

            QuantePagine = totPagine;
            return totPagine; 
        }//fM

        public IQueryable<T> QueryDati
        {
            get; set;
        }

        public bool RicalcolaPagine { get; set; }

        #endregion

    }//fC

}
